Skip to content

fix(devx): verdict handshake for check-platform-checklist and dispatch-gates self-tests - #14960

Merged
baozhoutao merged 1 commit into
mainfrom
claude/issue-13800-carry-forward-verdict-handshake
Sep 3, 2026
Merged

fix(devx): verdict handshake for check-platform-checklist and dispatch-gates self-tests#14960
baozhoutao merged 1 commit into
mainfrom
claude/issue-13800-carry-forward-verdict-handshake

Conversation

@baozhoutao

Copy link
Copy Markdown
Contributor

Part of #13800

Hole 2 (the verdict handshake) for the four files that card carries forward. Two of the four needed no work — see the falsified premise below — so this PR repairs two and reports measurements for the other two. Fixes is deliberately not used: two rows are delivered as readings, and the card should stay open for triage to close.

Verified at b12a5bda (the head of this branch).

The premise, re-derived — and partly falsified

The card's acceptance criterion was the guard form !== SELF_TEST_VERDICT, with a positive control of 81 files on origin/main, each of the four returning 0.

That marker gives a false zero on two of the four. A second, equally deliberate spelling landed in commit 9acddde9 ("Verdict handshake for 134 scripts/** self-tests that exit 0 on an early return", #14479) — a module-level flag, chosen with the rationale stated in the code: "The self-test's own exit code stays load-bearing, so the handshake is a flag rather than a returned sentinel." Counting only the sentinel cannot see it.

A marker that separates before from after, proven across that commit:

file !== SELF_TEST_VERDICT | ReachedVerdict at 9acddde9^ at 9acddde9
check-durability-degradation-log-level.mjs 0 6
check-dispatcher-error-vocabulary.mjs 0 3
check-platform-checklist.mjs 0 0
scripts/pm/dispatch-gates.mjs 0 0

Union control on origin/main: 81 sentinel files + 77 flag files = 158 carrying a handshake in one spelling or the other.

Per-row outcome, each one measured rather than read

Probes inject return; (or a partial early verdict) at a hand-chosen anchor, write the mutation to a copy beside the original — the tracked file is never mutated — prove the mutation is on disk before running, and delete the copy in a finally.

1. scripts/pm/dispatch-gates.mjs — was NOT MEASURED, now measured, defeated, repaired

The card's NOT MEASURED was a property of the instrument, not the file. measure-self-test-floor.mjs's injectEarlyReturn() anchors on the first function selfTest() { in the source; in this file that match is inside a fixture string (a template literal), so the injection only ever produces a SyntaxError. The real definition is at line 10517 and the dispatch was:

if (process.argv.includes('--self-test')) {
  selfTest();          // <- result discarded
}

Anchored on the real definition, the anchor is unique (1 match) and the reading is clean:

exit output
unmodified, before repair 0 ✓ dispatch-gates self-test: 1288 cases pass.
early return, before repair 0 0 bytes
unmodified, after repair 0 ✓ dispatch-gates self-test: 1288 cases pass.
early return, after repair 1 ✗ dispatch-gates self-test: selfTest() returned without reaching its verdict...

Repaired with the landed sentinel form. ENTRY_BY_HAND in measure-self-test-floor.mjs is deliberately left alone: its note stays true of the mechanical probe after this repair, and that file is outside this card's surface.

2. scripts/check-platform-checklist.mjs — was NOT MEASURED, now measured, defeated, repaired

Five batteries returning { checked, failures }, combined by the caller. Both failure shapes the card predicted, measured before the repair:

mutation in selfTestTrapVocabulary exit what it printed
return; (bare) 1 TypeError: Cannot read properties of undefined (reading 'failures') — a crash a one-function probe reads as a handshake
return { failures: [], checked: 0 }; 0 ✓ ... 119 assertions (down from 141) — and still claiming in prose that the skipped direction "REFUSES an empty/renamed/reshaped" table

After the repair, all three mutations exit 1 and name the battery that did not finish:

✗ check-platform-checklist self-test: selfTestTrapVocabulary() returned without reaching its verdict,
so its assertions did not all run and no failure of theirs could be reported.

— and the same for selfTestSourceLineCitations(), confirming a late battery is caught too, not just the first.

Spelling: the flag form, not the sentinel, for exactly the reason the two sibling files state — the return value is load-bearing here (it carries checked and failures), so the handshake cannot be the return value. The guard is checked on both legs, including the inline one that runs on every invocation (this gate has no --self-test leg in CI), and it is placed before the .failures read so a bare return reports itself instead of crashing its reader. One requireReachedVerdict() helper carries the landed wording to ten call sites (five batteries × two legs); the check, the message and the exit code are the landed ones — only the tenfold duplication of the prose is factored out.

3. scripts/check-durability-degradation-log-level.mjsalready repaired; untouched

Received hole 2 in 9acddde9 (flag spelling). Both of its entries probed, and both hold:

  • selfTest → exit 1, ✗ ... selfTest() returned without reaching its verdict
  • selfTestReadSeams → exit 1

4. scripts/check-dispatcher-error-vocabulary.mjsalready repaired; untouched

Also from 9acddde9. Probed: exit 1 with the named diagnostic. Its hole 1 (SELF_TEST_BATTERIES, 7 hits, from #13799) is left strictly alone — the two counts are not merged.

The derived list, and the irony

Derived from this worktree after the edits, with --repo asserted:

node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack

One of the two repaired files IS that script. Re-derived both ways on identical explicit inputs — the base version of the script and the repaired one — the derived list is IDENTICAL. The repair changes the self-test handshake, not the derivation.

All 21 derived families re-run at b12a5bda and green, including pnpm check:pm-dispatch-gates (1288 cases) and pnpm check:parse-guard / check:entry-guard / check:nul-bytes. Every exit code was captured before any pipe.

Two derived entries were NOT MEASURED rather than red, both prerequisite refusals and neither a finding:

  • node scripts/check-test-completeness.mjs → exit 3, PREREQUISITE NOT MET — this gate grades a saved turbo run test log, and no log was named. Its own --self-test runs clean (exit 0).
  • node scripts/check-durability-degradation-log-level.mjs on a fresh worktree → exit 3 before pnpm install; re-run after install, exit 0.

One gate is red, and it is red on main too

pnpm check:platform-checklist exits 1 with 4 UNCLASSIFIED coverage findings (batch_endpoints, crud_endpoints, metadata_endpoints, route_generation). Not caused by this PR — the ca3fd4b1 version of the file, run in the same tree, produces the byte-identical four findings (diff of the two finding sets is empty). This gate is deliberately excluded from CI (.github/workflows/lint.yml: "validated by pnpm check:platform-checklist, but by MAINTAINER action"), which is why the drift accumulated unseen. Filed separately rather than fixed here.

skip-changeset: scripts/** publishes nothing.


🤖 Generated with Claude Code

https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk


Generated by Claude Code

…h-gates self-tests

Both dispatches discarded their self-test's completion, so a `return` above the
verdict printed nothing and still exited 0 — an unfinished self-test reported as
a verified gate.

Measured before the repair, on this tree:

  * `scripts/pm/dispatch-gates.mjs` — an early return at the real definition of
    `selfTest()` took the run from "1288 cases pass" to ZERO bytes of output and
    exit 0. The card carried this row as NOT MEASURED because the mechanical
    probe anchors on the first `function selfTest() {` in the source, which here
    is a fixture STRING: the injection lands inside a template literal and only
    ever yields a SyntaxError. That is a limit of the instrument, not of the
    file; a hand-read anchor measures it in one run.

  * `scripts/check-platform-checklist.mjs` — five batteries returning
    `{ checked, failures }` and combined by the caller. A section that stops
    running took the verdict from 141 assertions to 119, exited 0, and still
    claimed in prose that the direction it had skipped "REFUSES an
    empty/renamed/reshaped" table. A bare `return` instead yields `undefined`
    and CRASHES the combine — an exit code alone reads that crash as a
    handshake rather than as the accident it is.

dispatch-gates takes the landed sentinel form. check-platform-checklist takes
the landed FLAG form (`check-durability-degradation-log-level.mjs`,
`check-dispatcher-error-vocabulary.mjs`) for the reason those files state: the
return value is load-bearing, so the handshake cannot BE the return value. Its
guard is checked on both legs — including the inline one, which is the only leg
CI reaches — before the `.failures` read, so a bare `return` now reports itself
instead of crashing its reader.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk
@claude claude Bot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 3, 2026
@github-actions github-actions Bot added the size/s label Sep 3, 2026
@baozhoutao
baozhoutao marked this pull request as ready for review September 3, 2026 13:51
@baozhoutao
baozhoutao enabled auto-merge September 3, 2026 13:51
@baozhoutao
baozhoutao added this pull request to the merge queue Sep 3, 2026
Merged via the queue into main with commit 8ea03a9 Sep 3, 2026
36 checks passed
@baozhoutao
baozhoutao deleted the claude/issue-13800-carry-forward-verdict-handshake branch September 3, 2026 14:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/s skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants